/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.ui.dialogs;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.ParameterableElement;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.TemplateableElement;

import com.cea.papyrus.core.PapyrusPlugin;

/**
 * 
 */
public class BindingAndNamespaceTreeSelectionDialog extends Dialog{

	/**
	 * Associated Behavior.
	 */
	protected Package selectedPackage;
	protected static final Image IMG_PACKAGE = PapyrusPlugin.getImage(PapyrusPlugin.UML_ICONS_16x16+"Package.gif");
//	protected static final Image IMG_PACKAGE_IMPORT = PapyrusPlugin.getImage(PapyrusPlugin.UML_ICONS_16x16+"PackageImport.gif");

	/**
	 * The selection tree.
	 */
	protected Tree tree;
	
	protected Table templateParametersTable ;

	protected Button addSubstitution ;
	
	protected Button delSubstitution ;
	
	protected Label errorMessage ;
	
	private Package model;
	private TemplateableElement template ;
	private java.util.List<TemplateParameterTableItem> templateParametersItem ;
	
	/** set of package that keeps tracks of all imported packages in the model */
	private Set<Package> importedPackages = new HashSet<Package>();
	
	/**
	 * 
	 * 
	 * @param model 
	 * @param parent 
	 */
	public BindingAndNamespaceTreeSelectionDialog(Shell parent, Package model, TemplateableElement template){
		super(parent);
		this.model=model;
		this.template=template ;
		this.templateParametersItem = new ArrayList<TemplateParameterTableItem>() ;
	}

	@Override
	protected void configureShell(Shell shell) {
		// TODO Auto-generated method stub
		super.configureShell(shell);
		shell.setText("Binding definition") ;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.dialogs.ElementImportTreeSelectionDialog#buildImportTreeList(org.eclipse.swt.widgets.TreeItem, org.eclipse.uml2.uml.Package)
	 */
	/**
	 * Creates the dialog area
	 * 
	 * @param parent composite
	 * 
	 * @return control
	 */
	protected Control createDialogArea(Composite parent){
		Composite composite = (Composite)super.createDialogArea(parent);
		
		Composite buttons = new Composite(composite, SWT.NONE) ;
		buttons.setLayout(new GridLayout(3,false)) ;
		Label bindingLabel = new Label(buttons,SWT.NONE) ;
		bindingLabel.setText("Define parameter substitutions:") ;
		addSubstitution = new Button(buttons, SWT.NONE); addSubstitution.setEnabled(false) ;
		addSubstitution.setImage(PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"Add_16x16.gif")) ;
		addSubstitution.addMouseListener(new AddButtonPressedListener()) ;
		delSubstitution = new Button(buttons, SWT.NONE); delSubstitution.setEnabled(false) ;
		delSubstitution.setImage(PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"Delete_16x16.gif")) ;
		delSubstitution.addMouseListener(new DeleteButtonPressedListener()) ;
		
		templateParametersTable = createTemplateParametersTable(composite, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL, template) ;
		
		Label packageSelectionLabel = new Label(composite,SWT.NONE) ;
		packageSelectionLabel.setText("Select parent package for the bound type to be created:") ;
		
		tree = new Tree(composite, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL);
		TreeItem item = new TreeItem(tree, SWT.NONE);
		item.setText(model.getName());
		item.setData(model);
		item.setImage(IMG_PACKAGE);
		
		errorMessage = new Label(composite, SWT.NONE) ;
		
		for (Iterator i = model.getPackagedElements().iterator(); i.hasNext() ; ) {
			Element e = (Element)i.next() ;
			if (e instanceof Package) {
				Package currentPackage = (Package)e;
				item = new TreeItem(tree, SWT.NONE);
				String name = currentPackage.getName();
				item.setText((name!=null)?name:"<undefined>");
				item.setData(currentPackage);
				item.setImage(IMG_PACKAGE);
				buildPackageTreeList(item, currentPackage);
			}
		}
		tree.setLayoutData(new GridData(300, 300));

		tree.addSelectionListener(new SelectionListener() {
			public void widgetSelected(SelectionEvent e){
				TreeItem[] selection = tree.getSelection();
				selectedPackage = (Package)selection[0].getData();
				getButton(IDialogConstants.OK_ID).setEnabled(isCompletelyBound(templateParametersTable)) ;
				updateErrorMessage() ;
			}

			public void widgetDefaultSelected(SelectionEvent e) {
			}
		});

		updateErrorMessage() ;
		
		return composite;
	}

	protected void updateErrorMessage() {
		String error = "" ;
		String parentPackageNotSelected = "" ;
		String bindingPartiallyDefined = "" ;
		if (tree.getSelection().length == 0) {
			error = "Error: " ;
			parentPackageNotSelected = "Target parent package must be selected. " ;
		}
		if (!isCompletelyBound(templateParametersTable)) {
			error = "Error: " ;
			bindingPartiallyDefined = "All parameters must be bound." ;
		}
		errorMessage.setText(error + parentPackageNotSelected + bindingPartiallyDefined) ;
	}
	
	@Override
	protected Control createButtonBar(Composite parent) {
		Control c = super.createButtonBar(parent);
		getButton(IDialogConstants.OK_ID).setEnabled(false) ;
		return c ;
	}

	/**
	 * This method builds the element tree widget containing all packageable elements of a given package.
	 * @param _package the package to explore
	 * @param elemTree tree parent of packageable elements
	 */
	protected void buildPackageTreeList(TreeItem elemTree, Package _package){
		Iterator elemIter = _package.getPackagedElements().iterator();
		while(elemIter.hasNext()) {
			Element elem = (Element) elemIter.next();
			if(elem instanceof Package){
				TreeItem item = new TreeItem(elemTree, SWT.NONE);
				String name = ((NamedElement)elem).getName();
				item.setText((name!=null)?name:"<undefined>");
				item.setData(elem);
				item.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(elem));
				buildPackageTreeList(item, (Package)elem);
			}
//			else if(elem instanceof Type){
//				TreeItem item = new TreeItem(elemTree, SWT.NONE);
//				String name = ((NamedElement)elem).getName();
//				item.setText((name!=null)?name:"<undefined>");
//				item.setData(elem);
//				item.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(elem));
//				if (elem instanceof TemplateableElement) {
//					TemplateableElement templateableElement = (TemplateableElement)elem ;
//					if (templateableElement.isTemplate()) {
//						buildTemplateTreeList(item, templateableElement);
//					}
//				}
//			} 
		}
	}

//	protected void buildTemplateTreeList(TreeItem elemTree, TemplateableElement templateableElement){
//		com.cea.utils.TemplateSignature templateSignatureUtil = new com.cea.utils.TemplateSignature(templateableElement.getOwnedTemplateSignature()) ;
//		Iterator ite= templateSignatureUtil.getAccessibleTypes().iterator();
//		while(ite.hasNext()){
//			Element element=((com.cea.utils.Type)ite.next()).getUml2Element();
//			TreeItem item = new TreeItem(elemTree, SWT.NONE);
//			item.setText(((NamedElement)element).getName());
//			item.setData(element);
//			item.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(element));
//		}
//	}
	/**
	 * Returns the selected package.
	 * 
	 * @return behavior Behavior
	 */
	public java.util.List getResult(){
		java.util.List result = null ;
		if (selectedPackage != null) {
			result = new ArrayList() ;
			result.add(selectedPackage) ;
			for (Iterator<TemplateParameterTableItem> i = templateParametersItem.iterator() ; i.hasNext() ; ) {
				result.add(i.next()) ;
			}
		}
		return result;
	}
	
	
	
	@Override
	protected void cancelPressed() {
		// TODO Auto-generated method stub
		super.cancelPressed();
		this.selectedPackage = null ;
	}



	@Override
	protected void okPressed() {
		// TODO Auto-generated method stub
		super.okPressed();
//		Object o = tree.getSelection()[0].getData() ;
//		this.selectedPackage = (Package)tree.getSelection()[0].getData() ;
	}



	public class TemplateParameterTableItem {
		
		private TemplateParameter parameter ;
		private ParameterableElement actual ;
		private TableItem tableItem ;
		
		public TemplateParameterTableItem(int style, TemplateParameter parameter, TableItem tableItem) {
			this.parameter = parameter ;
			if (this.parameter != null && this.parameter.getDefault() != null)
				this.actual = this.parameter.getDefault() ;
			this.tableItem = tableItem ;
		}
		
		public TemplateParameter getParameter() {
			return parameter ;
		}
		
		public ParameterableElement getActual() {
			return actual ;
		}
		
		public EClass getParameterKind() {
			if (parameter != null && parameter.getParameteredElement() != null)
				return parameter.getParameteredElement().eClass() ;
			return null ;
		}
		
		public void updateText() {
			String parameterName = "" ;
			String actualName = "" ;
			// computes the name of the parameter
			if (parameter != null) {
				if (parameter.getParameteredElement()!=null) {
					parameterName = ((NamedElement)parameter.getParameteredElement()).getName() ;
				}
			}
			// computes the name of the actual parameter
			if (actual != null) {
				actualName = ((NamedElement)actual).getName() ;
			}
			// updates the displayed text
			tableItem.setText(parameterName + " -> " + actualName) ;
			tableItem.setData(this) ;
		}
		
		public boolean isBound() {
			return actual != null ;
		}
		
		public void bind(ParameterableElement actual) {
			this.actual = actual ;
		}
		
		public void unBind() {
			this.actual = null ;
		}
	}
	
	private Table createTemplateParametersTable(Composite parent, int style, TemplateableElement template) {
		Table table = new Table(parent, style) ;
		table.addMouseListener(new ItemSelectionListener()) ;
		if (template.isTemplate()) {
			for (Iterator<TemplateParameter> i = template.getOwnedTemplateSignature().getParameters().iterator() ; i.hasNext() ; ) {
				TemplateParameter t = i.next() ;
				TableItem tableItem = new TableItem(table, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL);
				TemplateParameterTableItem data = new TemplateParameterTableItem(SWT.NONE, t, tableItem) ;
				data.updateText() ;
				templateParametersItem.add(data) ;
//				tableItem.addSelectionListener(new AddElementSelectionListener(org.eclipse.uml2.uml.TemplateParameterSubstitution.class, templateBinding, currentFormalParameter)) ;
			}
		}
		GridData data = new GridData(GridData.FILL_HORIZONTAL) ;
		table.setLayoutData(data) ;
		return table ;
	}
	
	private boolean isCompletelyBound(Table templateParametersTable) {
		boolean result = true ;
		for (int i = 0 ; i < templateParametersTable.getItems().length && result ; i++) {
			TemplateParameterTableItem item = (TemplateParameterTableItem)templateParametersTable.getItem(i).getData() ;
			result = result && item.isBound() ;
		}
		return result ;
	}
	
	protected void runUpdateButtonStatus(TableItem item) {
		TemplateParameterTableItem tableItem = (TemplateParameterTableItem)item.getData() ;
		addSubstitution.setEnabled(true) ;
		if (tableItem.isBound())
			delSubstitution.setEnabled(true) ;
		else
			delSubstitution.setEnabled(false) ;
	}
	
	/**
	 * Returns the item on which a mouse event happened.
	 * 
	 * @param event the mouse event that happened on the item
	 * 
	 * @return the retrieved item or <code>null</code> if no
	 * item is associated to the event
	 */
	protected TableItem getItem(MouseEvent event) {
		TableItem[] items = ((Table)event.getSource()).getSelection();
		if(items.length > 0) {
			return items[0];
		}
		return null;
	}
	
	/**
	 * listener to add an item from the table.
	 * 
	 * @author Remi Schnekenburger
	 */
	protected class ItemSelectionListener implements MouseListener {
		
		/**
		 * 
		 */
		public  ItemSelectionListener(){}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDoubleClick(MouseEvent e){
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDown(MouseEvent e){}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseUp(MouseEvent e){
			TableItem item = getItem(e);
			if(item != null) {
				runUpdateButtonStatus(item);
			}
		}
	};
	
	/**
	 * listener for the add button.
	 * 
	 * @author Remi Schnekenburger
	 */
	protected class AddButtonPressedListener implements MouseListener {
		
		/**
		 * 
		 */
		public  AddButtonPressedListener(){}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDoubleClick(MouseEvent e){
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDown(MouseEvent e){}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseUp(MouseEvent e){
			TableItem item = null;
			if (templateParametersTable.getSelection().length > 0)
				item = templateParametersTable.getSelection()[0] ;
			if (item != null) {
				TemplateParameterTableItem dataItem = (TemplateParameterTableItem)item.getData() ;
				EClass metaclassFilter = dataItem.getParameterKind() ;
				Classifier constrainingClassifier = null ;
				if (dataItem.getParameter() != null && dataItem.getParameter() instanceof ClassifierTemplateParameter) {
					ClassifierTemplateParameter p = (ClassifierTemplateParameter)dataItem.getParameter() ;
					constrainingClassifier = p.getConstrainingClassifier() ;
				}
				TypedElementTreeSelectionDialog dialog = new TypedElementTreeSelectionDialog(getParentShell(),model, dataItem.getParameterKind(), constrainingClassifier) ;
				dialog.open() ;
				if (dialog.getResult() != null) {
					dataItem.bind(dialog.getResult()) ;
					dataItem.updateText() ;
					getButton(IDialogConstants.OK_ID).setEnabled(isCompletelyBound(templateParametersTable) &&
																	tree.getSelection().length > 0) ;
				}
			}
			updateErrorMessage() ;
		}
	};
	
	/**
	 * listener for the add button.
	 * 
	 * @author Remi Schnekenburger
	 */
	protected class DeleteButtonPressedListener implements MouseListener {
		
		/**
		 * 
		 */
		public DeleteButtonPressedListener(){}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDoubleClick(MouseEvent e){
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDown(MouseEvent e){}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseUp(MouseEvent e){
			TableItem item = null;
			if (templateParametersTable.getSelection().length > 0)
				item = templateParametersTable.getSelection()[0] ;
			if (item != null) {
				TemplateParameterTableItem dataItem = (TemplateParameterTableItem)item.getData() ;
				dataItem.unBind() ;
				if (dataItem.getParameter().getDefault() != null) {
					dataItem.bind(dataItem.getParameter().getDefault()) ;
					getButton(IDialogConstants.OK_ID).setEnabled(isCompletelyBound(templateParametersTable) &&
							tree.getSelection().length > 0) ;
				}
				else
					getButton(IDialogConstants.OK_ID).setEnabled(false) ;
				dataItem.updateText() ;
			}
			updateErrorMessage() ;
		}
	
	};
}
